//
// @Auther: by wujehy 
// @Email:wujehy@qq.com
// @Data:20-4-14
// @Time:上午11:06
//

#include <sstream>
#include "AuthBusiness.h"
#include "GeeJoan/AuthModule.pb.h"
#include "DataBase/DataBaseHelper.h"
#include "GeeJoan/NetWorkPackage.pb.h"
#include "ServerContext.h"
#include "Tools/GeeJoanMD5.h"
#include "Tools/CommonTool.h"

using namespace GeeJoan::Protocol;
using namespace GeeJoan::Protocol::Auth;


static std::string genLoginToken(std::string &user, std::string &password)
{
    std::stringstream ss;
    ss << "Token." << user << "." << password << "." << util::timeMilli();
    return StringToHex( ss.str());
}
//
//AuthBusiness::AuthBusiness( int type_sub, int taskid, const TcpConnPtr &con, uint8_t *protoData, int datalen, void *userdata)
//{
//    currSubType = type_sub;
//    currMod = NetWork::NetWorkPackageModel::AuthMod;
//
//    currTaskid = taskid;
//
////    call500(con,"error AuthBusiness Register");
//    switch (type_sub)
//    {
//        case 1: // Register
//        {
//            this->Register(context, protoData, datalen, userdata );
//            break;
//        }
//        case 2:
//        { // lgoin
//            this->Login(context, protoData, datalen, userdata );
//            break;
//        }
//        case 3: // logout
//        {
//            this->LogOut( con, protoData, datalen, userdata );
//            break;
//        }
//        case 4:
//        { //HeartBeat
//            this->HeartBeat( con, protoData, datalen, userdata );
//            break;
//        }
//        case 5:
//        { // change
//            this->ChangePassword( con, protoData, datalen, userdata );
//            break;
//        }
//        default:
//            info( "undefine type Auth" );
//            break;
//    }
//
//
//}

int AuthBusiness::Register(EventContext *context, uint8_t *protoData, int datalen, void *userdata)
{

    RegisterAPI registerApi;
    if (!registerApi.ParseFromArray( protoData, datalen ))
    {
        // 失败
        call500( context, "error" );
        return -1;
    }

    DataBaseHelper dataBaseHelper( DATABASENAME );
    std::string username = registerApi.username();
    std::string srcPassword = registerApi.password();
    std::string password = GeeJoanMD5( srcPassword ).toString();
    std::string email = registerApi.email();
    int ret = dataBaseHelper.getUserDataBaseFactory()->addUser( username, password, email );

    std::string outstring;
    if (ret == EXIT_SUCCESS)
    {
        outstring = "";
        call200( context, outstring );
    } else
    {
        call500( context, "errmsg" );
    }

    // 成功

    return 0;
}

int AuthBusiness::Login(EventContext *context, uint8_t *protoData, int datalen, void *userdata)
{

    LoginRequest loginRequest;

    if (!loginRequest.ParseFromArray( protoData, datalen ))
    {
        // 失败
        call500( context, "error" );
        return -1;
    }

    DataBaseHelper dataBaseHelper( DATABASENAME );
    std::string username = loginRequest.username();

    std::string srcPassword = loginRequest.password();
    std::string password = GeeJoanMD5( srcPassword ).toString();
    int64_t currentTimeClient = loginRequest.timestamp();
    int64_t currentTimeServer = util::timeMilli();
    if ((currentTimeServer - currentTimeClient) > 20 * 1000) // 20 s
    {
        //timeout
        call500( context, "timeout" );
        return -1;
    }

    int ret = dataBaseHelper.getUserDataBaseFactory()->checktUser( username, password );

    std::string outstring;
    if (ret == EXIT_SUCCESS)
    {
        int64_t uid = dataBaseHelper.getUserDataBaseFactory()->getUidByUsername( username );
        outstring = "";
        LoginRespose loginRespose;
        std::string token = genLoginToken( username, password );
        context->con->context<ServerContext>().setToken( token );
        context->con->context<ServerContext>().setUsername( username );
        context->con->context<ServerContext>().setUid( uid );

        loginRespose.set_token( token );
        loginRespose.SerializePartialToString( &outstring );
        call200( context, outstring );
    } else
    {
        call500( context, "errmsg" );
        return -1;
    }

    return 0;
}

int AuthBusiness::LogOut(EventContext *context, uint8_t *protoData, int datalen, void *userdata)
{
    LogoutAPI logoutApi;
    if (!logoutApi.ParseFromArray( protoData, datalen ))
    {
        call500( context, "errmsg" );
        return -1;
    }
    // 成功
    std::string userToken = logoutApi.token();
    std::string serverToken = context->con->context<ServerContext>().getToken();
    int64_t userTime = logoutApi.timestamp();
    int64_t serverTime = util::timeMilli();

    bool isok = ((serverTime - userTime) < 20 * 1000 && userToken == serverToken);
    if (isok)
    {
        context->con->context<ServerContext>().setToken( "" );
        context->con->context<ServerContext>().setUsername( "" );
        context->con->context<ServerContext>().setUid( 0 );

        std::string outstring = "";
        call200( context, outstring );
    } else
    {
        call500( context, " not found your token or timeout! " );
        return -1;
    }

    return 0;
}

int AuthBusiness::HeartBeat(EventContext *context, uint8_t *protoData, int datalen, void *userdata)
{
    return 0;
}


int AuthBusiness::ChangePassword(EventContext *context, uint8_t *protoData, int datalen, void *userdata)
{
    bool islogin = context->con->context<ServerContext>().getToken().empty();

    if (islogin)
    {
        call500( context, " your not login " );
        return -1;
    }

    ChangePasswordRequest changePasswordRequest;
    if (!changePasswordRequest.ParseFromArray( protoData, datalen ))
    {
        // 解包失败
        call500( context, "error" );
        return -1;
    }
    // 成功

    std::string serverToken = context->con->context<ServerContext>().getToken();
    std::string userToken = changePasswordRequest.oldtoken();
    std::string newpassword = GeeJoanMD5( changePasswordRequest.newpassword()).toString();
    int64_t userTime = changePasswordRequest.timestamp();
    int64_t serverTime = util::timeMilli();
//    std::cout <<"serverTime - userTime = "<<serverTime << " , " << userTime <<std::endl;
    info( "serverTime - userTime , %ld , %ld", serverTime, userTime );
    bool isok = ((serverTime - userTime) < 20 * 1000 && userToken == serverToken);
    if (!isok)
    {
        call500( context, " not found your token or timeout! " );
        return -1;
    }

    // 参数正确
    DataBaseHelper dataBaseHelper( DATABASENAME );
    std::string username = context->con->context<ServerContext>().getUsername();
    int ret = dataBaseHelper.getUserDataBaseFactory()->changePassword( username, newpassword );

    if (ret == EXIT_SUCCESS)
    {
        ChangePasswordRespose changePasswordRespose;
        std::string newToken = genLoginToken( username, newpassword );
        changePasswordRespose.set_newtoken( newToken );
        context->con->context<ServerContext>().setToken( newToken );
        std::string outstring;
        changePasswordRespose.SerializeToString( &outstring );
        call200( context, outstring );

    } else
    {
        call500( context, "change fail" );
        return -1;
    }
    return 0;
}

AuthBusiness::AuthBusiness()
{
    info( "init AuthBusiness " );
}

AuthBusiness::~AuthBusiness()
{
    info( "delete AuthBusiness " );
}

int AuthBusiness::dispatchEvents(
        int type_sub,
        int taskid,
        const TcpConnPtr &con,
        uint8_t *protoData,
        int datalen,
        void *userdata)
{
    auto findEvent = this->eventMap.find( type_sub );

    int ret = ERROR_FAIL;
    if (findEvent != this->eventMap.end())
    {
        EventContext eventContext {
                .mod_type = NETWORK_MOD_TYPE_AUTH,
                .sub_type = type_sub,
                .taskid = taskid,
                .con = con
        };
        ret = findEvent->second( &eventContext, protoData, datalen, userdata );
    } else
    {
        info( "AuthBusiness::dispatchEvents , not found event , type = %d ", type_sub );
    }


    return ret;
}

void AuthBusiness::setEvents()
{
    this->eventMap.insert( std::pair<int, EventFunction>( AUTH_REGISTER, this->Register ));
    this->eventMap.insert( std::pair<int, EventFunction>( AUTH_LOGIN, this->Login ));
    this->eventMap.insert( std::pair<int, EventFunction>( AUTH_LOGOUT, this->LogOut ));
    this->eventMap.insert( std::pair<int, EventFunction>( AUTH_CHANGE_PASSWORD, this->ChangePassword ));
}
